import type { SeriesRow } from '../shared/types.js';

export function computeDailyReturns(closes: number[]): number[] {
  const returns: number[] = Array(closes.length).fill(NaN);
  for (let i = 1; i < closes.length; i++) {
    if (closes[i - 1] !== 0) {
      returns[i] = Math.log(closes[i] / closes[i - 1]);
    }
  }
  return returns;
}

export function computeAnnualizedVolatility(dailyReturns: number[], period: number): number[] {
  const out: number[] = Array(dailyReturns.length).fill(NaN);
  if (dailyReturns.length < period) return out;

  for (let i = period - 1; i < dailyReturns.length; i++) {
    const window = dailyReturns.slice(i - period + 1, i + 1);
    const sum = window.reduce((a, b) => a + b, 0);
    const mean = sum / period;
    const sumOfSquares = window.reduce((a, b) => a + Math.pow(b - mean, 2), 0);
    const stdDaily = Math.sqrt(sumOfSquares / period);
    out[i] = stdDaily * Math.sqrt(252); // Annualize with 252 trading days
  }
  return out;
}

export function rollingMax(values: number[], period: number): number[] {
  const out: number[] = Array(values.length).fill(NaN);
  const deque: number[] = [];
  for (let i = 0; i < values.length; i++) {
    while (deque.length && deque[0] <= i - period) deque.shift();
    while (deque.length && values[deque[deque.length - 1]] <= values[i]) deque.pop();
    deque.push(i);
    if (i >= period - 1) out[i] = values[deque[0]];
  }
  return out;
}

export function rollingMin(values: number[], period: number): number[] {
  const out: number[] = Array(values.length).fill(NaN);
  const deque: number[] = [];
  for (let i = 0; i < values.length; i++) {
    while (deque.length && deque[0] <= i - period) deque.shift();
    while (deque.length && values[deque[deque.length - 1]] >= values[i]) deque.pop();
    deque.push(i);
    if (i >= period - 1) out[i] = values[deque[0]];
  }
  return out;
}

export function prefixMax(values: number[]): number[] {
  const out: number[] = Array(values.length).fill(NaN);
  let m = -Infinity;
  for (let i = 0; i < values.length; i++) { m = Math.max(m, values[i]); out[i] = m; }
  return out;
}

export function prefixMin(values: number[]): number[] {
  const out: number[] = Array(values.length).fill(NaN);
  let m = Infinity;
  for (let i = 0; i < values.length; i++) { m = Math.min(m, values[i]); out[i] = m; }
  return out;
}

export function attachMetrics(series: SeriesRow[], holdingDays: number): SeriesRow[] {
  if (series.length === 0) return series;

  const closes = series.map(d => d.close);
  const highs = series.map(d => d.high);
  const lows = series.map(d => d.low);

  const dailyReturns = computeDailyReturns(closes);
  const annualizedVolatility = computeAnnualizedVolatility(dailyReturns, holdingDays); // Use holdingDays as period for STD

  const rh = rollingMax(highs, holdingDays);
  const rl = rollingMin(lows, holdingDays);

  return series.map((d, i) => ({
    ...d,
    annualized_volatility: isNaN(annualizedVolatility[i]) ? null : Number(annualizedVolatility[i].toFixed(6)),
    rolling_high: isNaN(rh[i]) ? null : Number(rh[i].toFixed(6)),
    rolling_low: isNaN(rl[i]) ? null : Number(rl[i].toFixed(6)),
  }));
}